package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;

import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.enums.LessonStatus;
import com.tianji.learning.domain.enums.PlanStatus;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.tianji.learning.domain.enums.LessonStatus.LEARNING;
import static com.tianji.learning.domain.enums.LessonStatus.NOT_BEGIN;
import static com.tianji.learning.domain.enums.PlanStatus.PLAN_RUNNING;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-19
 */
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;

    /**
     * 1、添加课程到课表
     * @param userId
     * @param courseIds
     */
    @Override
    @Transactional
    public void addLessons(Long userId, List<Long> courseIds) {
        // 1、根据课程ids集合远程调用查询课程信息
        // mq白学了，mq本地重试次数耗尽会扔到错误队列，开始错误失败策略
        // 第一种默认直接丢了，第二种重新入队不断重试，第三种扔给错误交换机和对应的路由key
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        // 判空没意识
        if (ObjectUtil.isEmpty(simpleInfoList)) {
            log.error("课程信息不存在，无法把课程添加到课表");
            return;
        }

        List<LearningLesson> learningLessons = new ArrayList<>();
        // 2、循环组装课表数据
        for (CourseSimpleInfoDTO simpleInfo : simpleInfoList) {
            LearningLesson learningLesson = new LearningLesson();
            learningLesson.setUserId(userId);
            learningLesson.setCourseId(simpleInfo.getId());

            // 2.2、填充课程过期时间
            Integer validDuration = simpleInfo.getValidDuration();
            // 判有效时间没意识
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            learningLesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            learningLessons.add(learningLesson);
        }

        // 3、批量新增
        this.saveBatch(learningLessons);
    }

    /**
     * 2、分页查询我的课表
     *
     * @param pageQuery 分页查询参数对象
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> pageQueryMyLessons(PageQuery pageQuery) {
        // 1、获取当前登录人
        Long  userId = UserContext.getUser();
        // 2、发起分页查询
        Page page = this.lambdaQuery().eq(LearningLesson::getUserId, userId).page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();

        if (ObjectUtil.isEmpty(page)) {
            return PageDTO.empty(page);
        }

        // 3、发起远程调用，查询课程详细信息（课程名称、封面、总课时数）
        // 3.1、收集当前页面的所有课程ID集合
        Map<Long, CourseSimpleInfoDTO> mp = getLongCourseSimpleInfoDTOMap(records);

        // 3.4、组装数据
        List<LearningLessonVO> vos = new ArrayList<>(records.size());
        for (LearningLesson lesson : records) {
            // 4.1、利用工具将lesson属性拷贝至vo中
            LearningLessonVO learningLessonVO = BeanUtils.copyBean(lesson, LearningLessonVO.class);

            // 4.2、获取课程特有信息（课程名称、封面、总课时数），填充至vo
            CourseSimpleInfoDTO courseInfo = mp.get(learningLessonVO.getCourseId());
            learningLessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
            learningLessonVO.setCourseName(courseInfo.getName());
            learningLessonVO.setSections(courseInfo.getSectionNum());

            // 存入vo集合
            vos.add(learningLessonVO);
        }

        // 封装最终的PageDTO并返回
        return PageDTO.of(page, vos);
    }

    /**
     * 删除课表中的课程
     *
     * @param userId
     * @param courseIds
     */
    @Override
    public void deleteLessons(Long userId, List<Long> courseIds) {
        this.lambdaUpdate()
                .eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getCourseId, courseIds)
                .remove();
    }

    /**
     * 根据id删除课表中的课程
     *
     * @param courseId
     */
    @Override
    public void deleteLesson(Long courseId) {
        this.lambdaUpdate()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getCourseId, courseId)
                .remove();
    }

    /**
     * 查询最近正在学习的课程
     *
     * @return
     */
    @Override
    public LearningLessonVO queryNow() {
        // 根据最近学习时间排个序查询最新的课程
        // 打日志的微习惯、判空、加事务、stream流、BeanUtils工具类
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .eq(LearningLesson::getStatus, 1)
                .last("limit 1").one();
        if (ObjectUtil.isEmpty(learningLesson)) {
            return null;
        }

        // 查课程总数
        LearningLessonVO learningLessonVO = BeanUtils.copyBean(learningLesson, LearningLessonVO.class);
        // 是否还有剩余的字段需要填充(多的是)
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(learningLesson.getCourseId(), false, false);
        if (ObjectUtil.isEmpty(courseInfoById)) {
            return null;
        }
        // 有没有什么 BeanUtil 可以多次填充直至填满
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(List.of(learningLesson.getLatestSectionId()));
        if (ObjectUtil.isEmpty(cataSimpleInfoDTOS)) {
            throw new BizIllegalException("目录信息不存在，无法查询课表");
        }
        learningLessonVO.setCourseName(courseInfoById.getName());
        learningLessonVO.setCourseCoverUrl(courseInfoById.getCoverUrl());
        learningLessonVO.setSections(courseInfoById.getSectionNum());

        // 设置两天小节名和小节 id
        learningLessonVO.setLatestSectionName(cataSimpleInfoDTOS.get(0).getName());
        learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTOS.get(0).getCIndex());

        Integer num = this.lambdaQuery().eq(LearningLesson::getUserId, UserContext.getUser()).count();
        learningLessonVO.setCourseAmount(num);

        return learningLessonVO;
    }

    /**
     * 校验接口是否有效
     *
     * @param courseId
     * @return
     */
    @Override
    public Long getIsLessonValid(Long courseId) {
        LearningLesson one = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .one();
        if (LocalDateTime.now().isBefore(one.getExpireTime())) {
            return 1L;
        } else {
            this.lambdaUpdate()
                    .eq(LearningLesson::getCourseId, courseId)
                    .eq(LearningLesson::getUserId, UserContext.getUser())
                    .set(LearningLesson::getStatus, 3)
                    .update();
            return 0L;
        }
    }

    /**
     * 收集records中的courseId，进行远程调用，获得课程信息并转为Map
     * @param records 课表信息集合
     * @return courseId -> course
     */
    private Map<Long, CourseSimpleInfoDTO> getLongCourseSimpleInfoDTOMap(List<LearningLesson> records) {
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());

        // 3.2、发起远程调用
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);

        // 3.3、健壮性判断
        if (ObjectUtil.isEmpty(courseInfoList)) {
            throw new BizIllegalException("课程信息不存在");
        }

        Map<Long, CourseSimpleInfoDTO> mp = courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        return mp;
    }

    /**
     * 创建学习计划
     */
    @Override
    public void createPlans(LearningPlanDTO planDTO) {
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(planDTO));
        if (ObjectUtil.isEmpty(lesson)) {
            throw new BizIllegalException("课程信息不存在");
        }
        this.lambdaUpdate().eq(LearningLesson::getId, lesson.getId())
                .set(LearningLesson::getWeekFreq, planDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PLAN_RUNNING)
                .update();

    }

    /**
     * 8、查询我的学习计划 (Day3- 随堂4)
     *
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery pageQuery) {
        LearningPlanPageVO vo  = new LearningPlanPageVO();
        // 1、获取当前登录人ID
        Long userId = UserContext.getUser();

        // 2、分页查询我的课表
        // select * from learning_lesson where user_id = #{userId} and plan_status = 1 and status in (0, 1) order by latest_learn_time desc limit 0,10
        Page<LearningLesson> pageResult = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PLAN_RUNNING)
                .in(LearningLesson::getStatus, NOT_BEGIN, LEARNING)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = pageResult.getRecords();
        if(ObjectUtil.isEmpty(records)){
            return vo.emptyPage(pageResult);
        }

        // 3、根据课程ID集合查询课程信息
        Map<Long, CourseSimpleInfoDTO> couseInfoMap = getLongCourseSimpleInfoDTOMap(records);

        // 4、统计本周实际学习小节数量
        // SELECT lesson_id,count(1) num FROM learning_record WHERE user_id = 2 AND finished = 1
        // AND finish_time BETWEEN '2025-03-23 00:00:00' AND '2025-03-30 23:59:59' group by lesson_id
        // 4.1、获取本周开始、结束日期
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());
        // 4.2、发起分组统计
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        // 学习课表微服务。用来统计本周学习小节数，指的是lessonId到本周学习小节数的一个map
        queryWrapper
                .select("lesson_id,count(1) num")
                .eq("user_id",userId)
                .eq("finished",true)
                .between("finish_time",begin,end)
                .groupBy("lesson_id");
        List<Map<String, Object>> mapList = recordMapper.selectMaps(queryWrapper);
        Map<Long, Long> learnedSectionNumMap = mapList.stream()
                .collect(Collectors.toMap(m -> (Long) m.get("lesson_id"), m -> (Long) m.get("num")));

        // 5、循环分页课表数据，填充缺失数据
        List<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            // 5.1、拷贝数据
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);

            // 5.2、填充课程数据
            CourseSimpleInfoDTO courseInfo = couseInfoMap.get(record.getCourseId());
            if(ObjectUtil.isNotEmpty(courseInfo)){
                learningPlanVO.setCourseName(courseInfo.getName());
                learningPlanVO.setSections(courseInfo.getSectionNum());
            }

            // 5.3、填充本周已学习小节数
            learningPlanVO.setWeekLearnedSections(learnedSectionNumMap.getOrDefault(record.getId(),0L).intValue());

            // 5.4、添加volist
            voList.add(learningPlanVO);
        }


        // 6、计算本周计划学习的总小节数
        // select sum(week_freq) from learning_lesson where user_id = #{userId} and plan_status = 1 and status in (0, 1)
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", userId)
                .eq("plan_status", PLAN_RUNNING)
                .in("status", NOT_BEGIN, LEARNING)
                .one();

        // 7、计算本周已学习的总小节数
        long sum = learnedSectionNumMap.values().stream().mapToLong(Long::longValue).sum();
        Integer weekFinished = Long.valueOf(sum).intValue();

        // 8、封装Vo并返回
        vo.setWeekTotalPlan(lesson.getTotalWeekfreq());
        vo.setWeekFinished(weekFinished);
        return vo.pageInfo(pageResult.getTotal(), pageResult.getPages(), voList);
    }

    /**
     * 构造用户 Id 和课程 Id 的 Lambda 查询 Wrapper
     * @param planDTO
     * @return
     */
    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(LearningPlanDTO planDTO) {
        return new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getCourseId, planDTO.getCourseId());
    }

}
